from flask import Flask, send_from_directory, render_template, request
from flask_socketio import SocketIO, join_room, rooms

from engine.dengine import DEngine
from engine.sessionmap import remote_session_map

from engine.io import *

engine = DEngine()

app = Flask(__name__,
            static_url_path='/static',
            )
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

detail_request_queue = {}


def remove_detail_request_queue(sid: str):
    if sid in detail_request_queue:
        detail_request_queue.pop(sid)


@app.route('/js/<path:path>')
def send_js(path):
    return send_from_directory('static/js', path)


@app.route('/css/<path:path>')
def send_css(path):
    return send_from_directory('static/css', path)


@app.route('/fonts/<path:path>')
def send_fonts(path):
    return send_from_directory('static/fonts', path)


@app.route('/img/<path:path>')
def send_img(path):
    return send_from_directory('static/img', path)


@app.route('/')
def index():  # put application's code here
    return render_template('index.html')


@socketio.on(ewm_model_list_request)
def on_ewm_model_list_request():
    io.send(emw_model_list_report, engine.model_list(), to=request.sid)


@socketio.on(ewm_model_detail_list_request)
def on_ewm_model_detail_list_request(model_list):
    nr_list = engine.detail_info_not_ready_list(model_list)
    if len(nr_list) == 0:
        io.send(emw_model_detail_list_report, engine.model_detail_list(model_list=model_list), to=request.sid)
    else:
        io.distributed_send(emd_model_detail_list_request, nr_list)
        detail_request_queue.update({
            request.sid: model_list
        })


@socketio.on(edm_model_detail_list_report)
def on_edm_model_detail_list_report(model_detail_list):
    engine.update_model_detail_list(request.remote_addr, model_detail_list)
    for sid in list(detail_request_queue):
        request_list = detail_request_queue[sid]
        if engine.detail_list_ready(request_list):
            io.send(emw_model_detail_list_report, engine.model_detail_list(request_list), to=sid)
            detail_request_queue.pop(sid)


@socketio.on(ewm_model_start_uploading)
def on_ewm_start_uploading(upload_data):
    """Web client requests for uploading

    Request data template::

        [
            {
                'model_name': '<model_name>'
                'file_list':
                [
                    {
                        'ftp': '<ftp_path>',
                        'local': '<local_path>'
                        'name': '<file_name>'
                    },
                    ...
                ]
            },
            ...
        ]

    :param upload_data:
    """
    engine.upload(upload_data)


@socketio.on(edm_model_state_report)
def on_edm_model_state_report(model_state_list):
    print(f'received model state report: {model_state_list}')
    engine.update_model_state(model_state_list)


@socketio.on(edm_model_start_releasing_report)
def on_edm_model_start_releasing_report(model):
    engine.init_model_state_running(model)
    io.web_send(emw_model_state_report, engine.model_state(model))


@socketio.on(ewm_model_start_releasing)
def on_ewm_start_releasing(models_settings):
    """
    :param models_settings:

    ::template
    ============================================
    {
        '<model_name>':
        {
            'model_name': '<model_name>'
            'make_type': '<make_type_id>'
            'repos_info':
            {
                '<repo_name>': '<branch_name>',
                ...
            },
            'release_info':
            {
                'new_feature': '<new_feature>',
                'bug_fixed': '<bug_fixed>',
                'changed': '<changed>'
                'note': '<note>'
            }
        },
        ...
    }
    =============================================

    :return:
    """
    print(f'start releasing with {models_settings}')
    engine.release(models_settings)


@socketio.on('connect')
def connect(auth):
    # global ready, mode
    print(f'#### {request.remote_addr} connected.')
    if auth and auth.get(DIO.room_distributed) == '1':
        print('---> Distributed client connected')
        if engine.client_exists(request.remote_addr):
            return False

        remote_session_map.update(
            request.remote_addr, request.sid
        )
        engine.add_remote(request.remote_addr, model_list=auth['model_list'])
        join_room(DIO.room_distributed)
        print(f'emit state list: {engine.model_state_list()}')
        io.web_send(emw_model_state_list_report, engine.model_state_list())
        io.web_send(emw_model_list_report, engine.model_list())
        io.web_send(emw_info_report, f'Distributed client {request.remote_addr} connected.')
    else:
        print('===> Web client connected')
        join_room(DIO.room_web)
        print(f'emit state list: {engine.model_state_list()}')
        io.send(emw_model_state_list_report, engine.model_state_list(), to=request.sid)
        io.send(emw_model_list_report, engine.model_list(), to=request.sid)


@socketio.on('disconnect')
def disconnect():
    rms = rooms()
    if DIO.room_distributed in rms:
        print(f'<--- {request.remote_addr} disconnected.')
        engine.remove_remote(request.remote_addr)
        remote_session_map.remove(request.remote_addr)
        print(f'emit state list: {engine.model_state_list()}')
        io.web_send(emw_model_state_list_report, engine.model_state_list())
        io.web_send(emw_model_list_report, engine.model_list())
        io.web_send(emw_info_report, f'Distributed client {request.remote_addr} disconnected.')
    else:
        remove_detail_request_queue(request.sid)
        print(f'<=== {request.remote_addr} disconnected.')


@socketio.on(edm_model_info_report)
def on_edm_model_info_report(info):
    io.web_send(emw_info_report, info)


@socketio.on(edm_model_error_report)
def on_edm_error_report(err):
    io.web_send(emw_model_error_report, err)


@socketio.on(edm_model_upload_state_report)
def on_edm_model_upload_state_report(upload_state_info):
    engine.update_model_upload_state_info(upload_state_info)
    io.web_send(emw_model_upload_state_report, upload_state_info)


if __name__ == '__main__':
    socketio.run(app,
                 host='10.20.30.34',
                 port=5000, debug=True)
